<?php

declare(strict_types=1);

namespace App\Resource\Service;

use App\Activity\Service\ActivityService;
use App\Common\Constants\ErrorCode;
use App\Common\Service\BaseService;
use App\Resource\Model\GoodsListModel;
use App\Resource\Model\GoodsModel;
use App\Resource\Model\ShopModel;
use App\Third\Service\Lsy\LsyShopGoodsPriceService;
use Hyperf\Utils\Coroutine\Concurrent;

class BuildGoodsService extends BaseService
{

    public function buildAllShopTimelyGoodsListToRedis(?array $lsyShopMap)
    {
        if (!$lsyShopMap) $lsyShopMap = ShopModel::query()->pluck('lsy_shop_no', 'shop_id')->toArray();
        $list = $this->getTimelyOrTomorrowGoodsList();
        $container = $this->container;
        $schemePrice = $container->get(LsyShopGoodsPriceService::class)->getAllSchemePrice();
        $actGoodsIdArr = $this->getActGoodsIdArr($lsyShopMap);
        $redis = $this->buildRedis;
        $concurrent = new Concurrent(30);
        foreach ($lsyShopMap as $shop_id => $lsy_shop_no){
            $schemePriceArr = [];
            foreach ($schemePrice as $shop_no_str => $item){
                if(strpos((string)$shop_no_str, $lsy_shop_no) !== false) $schemePriceArr = $item;
            }
            $concurrent->create(function () use ($list, $actGoodsIdArr, $shop_id, $lsy_shop_no, $redis, $schemePriceArr) {
                $nMap = [];
                $out = [];
                // 去除店铺下架，活动中的商品
                $actGoodsIdArr = array_key_exists($shop_id, $actGoodsIdArr) ? array_flip($actGoodsIdArr[$shop_id]):[];
                $redisStock = $redis->hGetAll("lsy:{$lsy_shop_no}:stock");//龙收银库存
                foreach ($list as $k => $goods){
                    if ( (strpos($goods['shop_ids'], (string)$shop_id) !== false) || isset($actGoodsIdArr[$goods['id']]))continue;
                    $surplusQuantity = isset($redisStock[$goods['goods_crm_code']]) ? $redisStock[$goods['goods_crm_code']]:0;
                    $ratio = (string)$goods['ratio'];
                    $goods['lsy_scheme_id'] = null;
                    if ($goods['read_pos_stock'] == 1){
                        // 读pos库存 变为份
                        if ($goods['scant_id'] == 1){
                            $pos_goods_stock = $ratio == 0 ? 0:bcdiv(bcmul((string)$surplusQuantity, '1000'), $ratio);
                        }else{
                            $pos_goods_stock = (string)floor($surplusQuantity);
                        }
                        // 可售卖库存
                        $can_buy_stock = bcsub($pos_goods_stock, (string)$goods['safety_stock']);
                        $goods['surplusQuantity'] = $can_buy_stock < 0 ? -1:$can_buy_stock;
                    }else{
                        // 系数为0 造成排序错乱
                        $goods['surplusQuantity'] =(string)$goods['number_stock'];
                    }
                    if ($goods['read_pos_price'] == 1){
                        if (array_key_exists($goods['goods_crm_code'], $schemePriceArr)){
                            $posSellingPrice = $schemePriceArr[$goods['goods_crm_code']]['price'];
                            $posMarketPrice = $schemePriceArr[$goods['goods_crm_code']]['stdPrice'];
                            $goods['lsy_scheme_id'] = $schemePriceArr[$goods['goods_crm_code']]['schemeID'];
                        }else{
                            $posSellingPrice = $goods['price_selling'];
                            $posMarketPrice = $goods['price_market'];
                            $goods['lsy_scheme_id'] = null;
                        }
                        // 读pos价格
                        if ($goods['scant_id'] == 1) {
                            // 非标品
                            $goods['price_selling'] = bcmul(bcdiv($posSellingPrice,'1000',6),$ratio,2);
                            $goods['price_market'] = bcmul(bcdiv($posMarketPrice,'1000',6),$ratio,2);
                        } else {
                            // 标品
                            $goods['price_selling'] = $posSellingPrice;
                            $goods['price_market'] = $posMarketPrice;
                        }
                    }
                    // 售罄
                    $goods['surplusQuantity'] <= 0 ? array_push($out, $goods) : array_push($nMap, $goods);
                }
                $map = array_merge(array_values($nMap),array_values($out) );
                $this->make($redis, $shop_id, $map);
            });
        }
        return true;
    }

    public function make($redis, $shop_id, $map, $timely = true){
        if ($timely){
            $key1 = "timely:shop:{$shop_id}:goods";
            $key2 = "timely:shop:{$shop_id}:goods:sort";
        }else{
            $key1 = "tomorrow:shop:{$shop_id}:goods";
            $key2 = "tomorrow:shop:{$shop_id}:goods:sort";
        }
        $pip = $redis->multi(2);
        $pip->del([$key1, $key2]);
        foreach ($map as $kk => $goods) {
            $val = $timely? [
                'surplusQuantity' => $goods['surplusQuantity'],
                'price_selling' => $goods['price_selling'],
                'price_market' => $goods['price_market'],
                'lsy_scheme_id' => $goods['lsy_scheme_id']
            ]:['number_sales' => $goods['number_sales'], 'goods_pick_time' => $goods['goods_pick_time']];
            $pip->hSet($key1, (string)$goods['id'], json_encode($val));
            $pip->zAdd($key2, $kk, $goods['id']);
        }
        $pip->exec();
    }


    public function buildAllShopTomorrowGoodsListToRedis(?array $lsyShopMap){
        if (!$lsyShopMap) $lsyShopMap = ShopModel::query()->pluck('lsy_shop_no', 'shop_id')->toArray();
        $list = $this->getTimelyOrTomorrowGoodsList(false);
        $takeDate = $this->container->get(GoodsService::class)->getTakeDate();
        $actGoodsIdArr = $this->getActGoodsIdArr($lsyShopMap, 'seckill');
        $redis = $this->buildRedis;
        $concurrent = new Concurrent(30);
        foreach ($lsyShopMap as $shop_id => $lsy_shop_no){
            $concurrent->create(function () use ($list, $actGoodsIdArr, $shop_id, $lsy_shop_no, $redis, $takeDate) {
                $nMap = [];
                // 活动中的商品
                $actGoodsIdArr = array_key_exists($shop_id, $actGoodsIdArr) ? array_flip($actGoodsIdArr[$shop_id]):[];
                foreach ($list as $k => $goods) {
                    // 去除店铺下架，活动中的商品
                    if ((strpos($goods['crd_shop_ids'], (string)$shop_id) !== false) || isset($actGoodsIdArr[$goods['id']])) continue;
                    $goods_pick_time = $goods['goods_pick_time'] ?? '00:00:00';
                    $goods['number_sales'] = $goods['number_sales'] + $goods['number_virtual'];
                    $goods['goods_pick_time'] = $takeDate . ' ' . substr($goods_pick_time, 0, 5);
                    array_push($nMap, $goods);
                }
                $this->make($redis, $shop_id, $nMap, false);
            });
        }
        return true;
    }

    /**
     * 及时达或次日达所有商品
     * @param bool $timely
     * @return \Hyperf\Database\Model\Builder[]|\Hyperf\Database\Model\Collection|\Hyperf\Database\Query\Builder[]|\Hyperf\Utils\Collection
     * @author 1iu
     * @date 2021-02-06 17:09
     */
    public function getTimelyOrTomorrowGoodsList(bool $timely = true){
        $container = $this->container;
        $gm = $container->get(GoodsModel::class);
        $glm = $container->get(GoodsListModel::class);
        $gmt = $gm->getTable();
        $glmt = $glm->getTable();
        $goodsField = ['goods.id','goods.title','goods.group_id','goods.goods_crm_code','goods.spell_num','goods.cate_id',
            'goods.scant_id','goods.ratio','goods.logo','goods.read_pos_price','goods.read_pos_stock',
            'goods.safety_stock','goods.kz_goods_id','goods.sort','goods.shop_ids','goods.crd_shop_ids'];
        $goodsListField = ['gl.goods_spec','gl.price_selling','gl.price_market','gl.number_stock','gl.number_virtual',
            'gl.number_sales','gl.commission','gl.cost_price','gl.goods_pick_time'];
        return $gm::query()
            ->from("{$gmt} as goods")
            ->leftJoin("{$glmt} as gl", 'goods.id', '=', 'gl.goods_id')
            ->select(array_merge($goodsField, $goodsListField))
            ->when($timely, function ($query){
                return $query->where(['goods.is_today' => '1','goods.group_id' => 0, 'goods.is_home' => 1]);
            },function ($query) {
                return $query->where([['goods.is_next_day', '=' , '1'],['gl.number_stock', '>', 0],['goods.is_home', '=', 1]]);
            })
            ->where(['goods.status' => 1,'goods.is_deleted' => 0])
            ->orderByDesc('goods.sort')
            ->get()->toArray();
    }

    public function buildGoodsListToRedis(string $attr){
        try {
            if ($attr == 'timely'){
                $list = $this->getTimelyOrTomorrowGoodsList(true);
                $key = 'timely:goods';
                $msg = '构建及时达公共商品缓存成功';
            }else{
                $list = $this->getTimelyOrTomorrowGoodsList(false);
                $key = 'tomorrow:goods';
                $msg = '构建次日达公共商品缓存成功';
            }

            $redis = $this->buildRedis;
            $pip = $redis->multi(2);
            $pip->del($key);
            foreach ($list as $kk => $goods) {
                $pip->hSet($key, (string)$goods['id'], json_encode($goods));
            }
            $pip->exec();
        }catch (\Exception $e){
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => $e->getMessage()];
        }
        return ['code' => 1, 'data'=>[], 'msg' => $msg];
    }

    /**
     * 组合所有店铺活动商品id
     * @param array $lsyShopMap
     * @param string $type
     * @return array
     * @author 1iu
     * @date 2021-02-06 17:10
     */
    public function getActGoodsIdArr(array $lsyShopMap, string $type = ''){
        $arr = $this->container->get(ActivityService::class)->getActGoodsIdsByAllShop($type);
        $map = [];
        foreach ($arr as $k => $v){
            if (!$v['goods_id']) continue;
            foreach ($lsyShopMap as $shop_id => $lsy_shop_no){
                if (strpos($v['shop_ids'], (string)$shop_id) !== false){
                    $map[$shop_id][] = $v['goods_id'];
                }
            }
        }
        return array_map(fn($v) => array_unique($v), $map);
    }

    /**
     * 删除店铺商品资料缓存
     * @param string $key
     * @return array
     * @author 1iu
     * @date 2021-02-06 17:11
     */
    public function delKeys(string $key){
        try {
            $redis = $this->buildRedis;
            $iterator = null;
            $dels = [];
            while(false !== ($del = $redis->scan($iterator,$key))) {
                foreach($del as $v) {
                    array_push($dels, $v);
                }
            }
            $count = $redis->del($dels);
            return ['code' => 1, 'data'=>['key_num' => $count], 'msg' => '清空及时达商品缓存成功'];
        }catch (\Exception $e){
            return ['code' => 0, 'errorCode' => ErrorCode::SYSTEM_INVALID, 'msg' => $e->getMessage()];
        }

    }

    public function createEvent(array $goods){
//        $goods = GoodsModel::query()->select(['id','group_id','ratio'])->where('id', $goods->goods_id)->first();
        $info = $this->getItemComb($goods);
        $redis = $this->buildRedis;
        if ($goods['status'] == 1){
            if ($goods['is_today'] == 1 and $goods['group_id'] == 0 and $goods['is_home'] == 1){
                $this->buildAllShopTimelyGoodsListToRedis(null);
                $redis->hSet('timely:goods', (string)$info['id'], json_encode($info));
            }
            if ($goods['is_next_day'] == 1 and $goods['number_stock'] > 0){
                $this->buildAllShopTomorrowGoodsListToRedis(null);
                $redis->hSet('tomorrow:goods', (string)$info['id'], json_encode($info));
            }
        }
    }

    public function updateEvent(array $goods, string $type){
        $redis = $this->buildRedis;
        if ($goods['is_today'] == 1 and $goods['group_id'] == 0){
            $commKey = 'timely:goods';
            $shopKey = 'timely:shop:*:goods*';
            if ($goods['status'] == 1 and $goods['is_home'] == 1){
                $info = $this->getItemComb($goods);
                $this->buildAllShopTimelyGoodsListToRedis(null);
                $redis->hSet($commKey, (string)$goods['id'], json_encode($info));
            }else{
                $redis->hDel($commKey, (string)$goods['id']);
                $this->updateEventDelRedisKey($redis, $shopKey, (string)$goods['id']);
            }
        }
        if ($goods['is_next_day'] == 1){
            $commKey = 'tomorrow:goods';
            $shopKey = 'tomorrow:shop:*:goods*';
            if ($goods['status'] == 1){
                $info = $this->getItemComb($goods);
                $this->buildAllShopTomorrowGoodsListToRedis(null);
                $redis->hSet($commKey, (string)$goods['id'], json_encode($info));
            }else{
                $redis->hDel($commKey, (string)$goods['id']);
                $this->updateEventDelRedisKey($redis, $shopKey, (string)$goods['id']);
            }
        }
    }

    protected function updateEventDelRedisKey($redis, $shopKey, $goods_id){
        $iterator = null;
        while(false !== ($keys = $redis->scan($iterator,$shopKey))) {
            foreach($keys as $key) {
                if($redis->type($key) == 4) $redis->zRem($key, $goods_id);
                if($redis->type($key) == 5) $redis->hDel($key, $goods_id);
            }
        }
    }

    protected function getItemComb(array $goods){
        return [
            'id' => $goods['id'],
            'title' => $goods['title'],
            'group_id' => $goods['group_id'],
            'goods_crm_code' => $goods['goods_crm_code'],
            'spell_num' => $goods['spell_num'],
            'cate_id' => $goods['cate_id'],
            'scant_id' => $goods['scant_id'],
            'ratio' => $goods['ratio'],
            'logo' => $goods['logo'],
            'read_pos_price' => $goods['read_pos_price'],
            'read_pos_stock' => $goods['read_pos_stock'],
            'safety_stock' => $goods['safety_stock'],
            'kz_goods_id' => $goods['kz_goods_id'],
            'sort' => $goods['sort'],
            'shop_ids' => $goods['shop_ids'],
            'crd_shop_ids' => $goods['crd_shop_ids'],
            'goods_spec' => $goods['goods_spec'],
            'number_virtual' => $goods['number_virtual'],
            'price_market' => $goods['price_market'],
            'price_selling' => $goods['price_selling'],
            'number_stock' => $goods['number_stock'],
            'number_sales' => $goods['number_sales'],
            'commission' => $goods['commission'],
            'cost_price' => $goods['cost_price'],
            'goods_pick_time' => $goods['goods_pick_time']
        ];
    }
}
